const WebSocket = require('ws');
let fetchFn = null;
async function getFetch() {
    if (typeof fetch !== 'undefined') return fetch.bind(globalThis);
    if (!fetchFn) {
        const mod = await import('node-fetch');
        fetchFn = mod.default;
    }
    return fetchFn;
}

function wrapHostForUrl(host) {
    if (!host) return 'localhost';
    if (host.startsWith('[') && host.endsWith(']')) return host;
    if (host.includes(':')) return `[${host}]`;
    return host;
}

class VipSelectDeputyTester {
    constructor({ host, port, mobile, passwordHash }) {
        this.host = host;
        this.port = port;
        this.mobile = mobile;
        this.passwordHash = passwordHash;
        this.token = '';
        this.ws = null;
        this.queue = [];
    }

    httpUrl(path) {
        const h = wrapHostForUrl(this.host);
        return `http://${h}:${this.port}${path}`;
    }

    async registerIfNeeded() {
        const fetch = await getFetch();
        try {
            const res = await fetch(this.httpUrl('/user-service/user/register'), {
                method: 'POST',
                headers: { 'content-type': 'application/json' },
                body: JSON.stringify({ mobile: this.mobile, pwd: this.passwordHash, captcha: '123456' }),
            });
            const data = await res.json().catch(() => ({}));
            console.log('register:', JSON.stringify(data));
        } catch (e) {
            console.log('register err:', e.message);
        }
    }

    async login() {
        await this.registerIfNeeded();
        const fetch = await getFetch();
        const res = await fetch(this.httpUrl('/user-service/user/login'), {
            method: 'POST',
            headers: { 'content-type': 'application/json' },
            body: JSON.stringify({ mobile: this.mobile, pwd: this.passwordHash }),
        });
        const body = await res.json();
        if (!(body && body.code === 0 && body.data?.accessToken)) throw new Error('login failed');
        this.token = body.data.accessToken;
        console.log('login ok');
    }

    async connect() {
        const h = wrapHostForUrl(this.host);
        const url = `ws://${h}:${this.port}/user-service/websocket?t=${this.token}`;
        await new Promise((resolve, reject) => {
            this.ws = new WebSocket(url);
            const to = setTimeout(() => { try { this.ws && this.ws.close(); } catch { } reject(new Error('ws timeout')); }, 10000);
            this.ws.on('open', () => { clearTimeout(to); resolve(); });
            this.ws.on('error', (e) => { clearTimeout(to); reject(e); });
            this.ws.on('message', (buf) => { try { const m = JSON.parse(buf.toString()); this.queue.push(m); } catch { } });
        });
        console.log('ws ok');
    }

    async sendWait(pred, payload, timeoutMs = 10000) {
        if (payload) this.ws.send(JSON.stringify(payload));
        const start = Date.now();
        while (Date.now() - start < timeoutMs) {
            for (let i = 0; i < this.queue.length; i++) {
                const m = this.queue[i];
                if (pred(m)) { this.queue.splice(i, 1); return m; }
            }
            await new Promise(r => setTimeout(r, 30));
        }
        throw new Error('timeout');
    }

    async selectFirstRole() {
        await new Promise(r => setTimeout(r, 800));
        const res = await this.sendWait(m => m.act_id === 3, { c: 3, d: null });
        const list = res.roleList || [];
        if (!list.length) throw new Error('no roles');
        const role = list[0];
        await this.sendWait(m => m.act_id === 4, { c: 4, d: { characterId: role.id } });
        console.log('role ok:', role.id);
    }

    async run() {
        await this.login();
        await this.connect();
        await new Promise(r => setTimeout(r, 1000));
        await this.selectFirstRole();
        // 6103 获取国士列表（choose=1）
        const listRes = await this.sendWait(m => m.act_id === 6103, { c: 6103, d: { pid: 1 } });
        console.log('6103 list count=', (listRes.list || []).length);
        if (!listRes.list || listRes.list.length === 0) throw new Error('6103 empty');
        const target = listRes.list[0];
        // 6154 领取所选副将
        const applyRes = await this.sendWait(m => m.act_id === 6154, { c: 6154, d: { pid: 1, npcid: target.id } });
        if (applyRes.code !== 0) throw new Error('6154 failed: ' + (applyRes.msg || ''));
        console.log('6154 ok:', applyRes.data);
        this.ws && this.ws.close();
        console.log('Done.');
    }
}

(async () => {
    const tester = new VipSelectDeputyTester({ host: '[::1]', port: 3000, mobile: '17091271111', passwordHash: 'e10adc3949ba59abbe56e057f20f883e' });
    try {
        await tester.run();
        process.exit(0);
    } catch (e) {
        console.error('test failed:', e.message);
        process.exit(1);
    }
})();


